<template>
  <div style="border: 1px solid #ccc; padding: 20px; width: 600px">
    <svg class="dagre" width="600" height="600">
      <g class="container"></g>
    </svg>
    <div ref="tooltip" class="tooltip">
      <div>节点ID：{{ currentNode.id }}</div>
      <div>节点名称：{{ currentNode.nodeName }}</div>
    </div>
  </div>
</template>

<script>
import dagreD3 from "dagre-d3";
import * as d3 from "d3";

export default {
  data() {
    return {
      currentNode: {
        id: null,
        nodeName: "",
      },
      nodes: [],
      edges: [],
      indexObj: {},
      tableList: [
        {
          name: "节点一",
          pre: [],
        },
        {
          name: "节点二",
          pre: ["节点一"],
        },
        {
          name: "节点三",
          pre: ["节点一"],
        },
        {
          name: "节点四",
          pre: ["节点二", "节点三"],
        },
        {
          name: "节点五",
          pre: ["节点四"],
        },
        {
          name: "节点六",
          pre: ["节点四"],
        },
        {
          name: "节点七",
          pre: ["节点五", "节点六"],
        },
      ],
    };
  },
  async mounted() {
    // 把表格的数据转成节点和线条
    const t1 = new Date().getTime();
    this.getData();
    await this.changeData();
    console.log("耗时：", new Date().getTime() - t1);
    // this.draw();
  },
  methods: {
    getData() {
      let maxLen = 1600;
      for (let i = 1; i <= maxLen; i++) {
        const obj = {
          name: `节点${i}`,
          pre: [],
        };
        if (i + 1 < maxLen) {
          obj.pre.push(`节点${i + 1}`);
        }
        this.tableList.push(obj);
      }
    },
    async changeData() {
      // 给每个节点设置对应的编号
      this.tableList.map((v, i) => {
        this.indexObj[v.name] = i;
      });
      await this.tableList.map(async (v, i) => {
        // 点
        this.nodes.push({
          id: i,
          nodeName: v.name,
          shape: "rect",
        });
        // 线
        let arr = await this.getLine(v);
        this.edges = this.edges.concat(arr);
      });
      this.draw();
    },
    getLine(node) {
      let brr = [];
      if (node.pre.length) {
        if (node.pre.length === 1) {
          brr.push({
            start: this.indexObj[node.pre[0]],
            end: this.indexObj[node.name],
            label: "",
          });
        } else {
          node.pre.map((v) => {
            brr.push({
              start: this.indexObj[v],
              end: this.indexObj[node.name],
              label: "",
            });
          });
        }
      }
      return brr;
    },
    // 绘制简单的流程图
    draw() {
      // 创建 Graph 对象
      const g = new dagreD3.graphlib.Graph()
        .setGraph({
          rankdir: "LR", // 流程图从下向上显示，默认'TB'，可取值'TB'、'BT'、'LR'、'RL'
        })
        .setDefaultEdgeLabel(function () {
          return {};
        });
      // Graph添加节点
      this.nodes.forEach((node) => {
        g.setNode(node.id, {
          id: node.id,
          label: node.nodeName,
          shape: node.shape, //节点形状，可以设置rect(长方形),circle,ellipse(椭圆),diamond(菱形) 四种形状，还可以使用render.shapes()自定义形状
          style: "fill:#fff;stroke:#70baff", //节点样式,可设置节点的颜色填充、节点边框 fill:#61b2e4;stroke:#fff
          labelStyle: "fill: #000;font-weight:bold", //节点标签样式, 可设置节点标签的文本样式（颜色、粗细、大小）fill: #fff;font-weight:bold
          rx: 5, // 设置圆角
          ry: 5, // 设置圆角
          paddingBottom: 15,
          paddingLeft: 20,
          paddingRight: 20,
          paddingTop: 15,
        });
      });

      // Graph添加节点之间的连线
      if (this.nodes.length > 1) {
        this.edges.forEach((edge) => {
          g.setEdge(edge.start, edge.end, {
            label: edge.label, //边标签
            style: "stroke: #70baff; fill: none; stroke-width: 2px", // 连线样式
            arrowheadStyle: "fill: #70baff;stroke: #70baff;", //箭头样式，可以设置箭头颜色
            arrowhead: "normal", //箭头形状，可以设置 normal,vee,undirected 三种样式，默认为 normal
          });
        });
      }

      // 获取要绘制流程图的绘图容器
      const container = d3.select("svg.dagre").select("g.container");

      // 创建渲染器
      const render = new dagreD3.render();
      // 在绘图容器上运行渲染器绘制流程图
      render(container, g);

      // 拖拽缩放
      const svg = d3.select("svg.dagre");
      let zoom = d3
        .zoom()
        .scaleExtent([0.5, 2])
        .on("zoom", (current) => {
          container.attr("transform", current.transform);
        });
      svg.call(zoom);

      // 鼠标悬停显示隐藏tooltip
      const that = this;
      const tooltipBox = that.$refs.tooltip;
      container
        .on("mouseover", (e) => {
          if (e.target.nodeName === "rect") {
            that.currentNode = that.nodes.filter(
              (item) => item.id === Number(e.target.__data__)
            )[0];
            tooltipBox.style.display = "block";
            tooltipBox.style.top = e.clientY + 20 + "px";
            tooltipBox.style.left = e.clientX + "px";
          }
        })
        .on("mouseout", function () {
          tooltipBox.style.display = "none";
        });
    },
  },
};
</script>

<style scoped>
.tooltip {
  position: absolute;
  font-size: 12px;
  background-color: white;
  border-radius: 3px;
  box-shadow: rgb(174, 174, 174) 0px 0px 10px;
  cursor: pointer;
  display: none;
  padding: 10px;
}

.tooltip > div {
  padding: 10px;
}
</style>
